﻿<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>重力坠落</title>

<style>
html,body {
	overflow:hidden;
	touch-action:none;
	position:absolute;
	margin:0;
	padding:0;
	width:100%;
	height:100%;
	background:#000;
}
canvas {
	position:absolute;
	width:100%;
	height:100%;
	user-select:none;
	background:#000;
}
</style>

</head>
<body>

<div></div>

<script>
"use strict";
class World {
    constructor(setup) {
        this.DEBUG = setup.DEBUG || false;
        this.gravity = setup.gravity || 50;
        this.iterations = setup.iterations || 10;
        this.timeStep = setup.timeStep || 1 / 30;
        this.invDT = 1 / this.timeStep;
        this.friction = setup.friction || 0.2;
        this.allowedPenetration = setup.allowedPenetration || 0.01;
        this.biasFactor = setup.biasFactor || 0.2;
        this.relativeTol = setup.relativeTol || 0.95;
        this.absoluteTol = setup.absoluteTol || 0.01;
        this.maxy = setup.maxy || 100000;
        this.shapes = {};
        this.bodies = [];
        this.joints = [];
        this.contacts = [];
        this.numContacts = 0;
        this.maxContacts = 0;
        this.ie = [vec(), vec()];
        this.c1 = [vec(), vec()];
        this.c2 = [vec(), vec()];
        this.bodyCount = 0;
        this.jointsCount = 0;
        this.dp = vec();
        this.n = vec();
        this.fn = vec();
        this.sn = vec();
    }
    addBody(setup) {
        const body = new World.Body(this, setup);
        this.bodies.push(body);
        this.bodyCount = this.bodies.length;
        return body;
    }
    addJoint(setup) {
        let joint = new World.Joint(this, setup);
        this.joints.push(joint);
        this.jointsCount = this.joints.length;
        return joint;
    }
    addShape(setup) {
        const shape = new World.Shape(setup);
        this.shapes[setup.id] = shape;
        return shape;
    }
    removeBody(body) {
        for (let i = 0; i < this.bodyCount; ++i) {
            if (this.bodies[i] === body) {
                this.bodies.splice(i, 1);
                for (let j = 0; j < this.jointsCount; ++j) {
                    const joint = this.joints[j];
                    if (joint.bA === body || joint.bB === body) {
                        this.joints.splice(j, 1);
                        this.jointsCount--;
                        j--;
                    }
                }
                break;
            }
        }
        this.bodyCount = this.bodies.length;
    }
    step() {
        let i, j, bi, bj, d, ct;
        this.numContacts = 0;
        for (i = 0; i < this.bodyCount; ++i) {
            bi = this.bodies[i];
            for (j = i + 1; j < this.bodyCount; ++j) {
                bj = this.bodies[j];
                if (bi.iM || bj.iM) {
                    this.dp.subFrom(bj.p, bi.p);
                    d = bi.rd + bj.rd;
                    if (this.dp.sqlen() < d * d) {
                        if (!bi.nocontact.includes(bj)) {
                            this.collide(bi, bj, this.dp);
                        }
                    }
                }
            }
        }
        for (i = 0; i < this.jointsCount; ++i) {
            this.joints[i].preStep();
        }
        for (j = 0; j < this.iterations; ++j) {
            for (i = 0; i < this.numContacts; ++i) {
                this.contacts[i].applyImpulse();
            }
            for (i = 0; i < this.jointsCount; ++i) {
                this.joints[i].applyImpulse();
            }
        }
        for (i = 0; i < this.bodyCount; ++i) {
            this.bodies[i].integrate();
        }
        for (i = 0; i < this.bodyCount; ++i) {
            this.bodies[i].draw();
        }
    }
    collide(bA, bB, dp) {
        let dax = bA.cos * dp.x + bA.sin * dp.y;
        let day = -bA.sin * dp.x + bA.cos * dp.y;
        let dbx = bB.cos * dp.x + bB.sin * dp.y;
        let dby = -bB.sin * dp.x + bB.cos * dp.y;
        let m00 = Math.abs(bA.cos * bB.cos + bA.sin * bB.sin);
        let m01 = Math.abs(-bA.sin * bB.cos + bA.cos * bB.sin);
        let m10 = Math.abs(-bA.cos * bB.sin + bA.sin * bB.cos);
        let m11 = Math.abs(bA.sin * bB.sin + bA.cos * bB.cos);
        let fAx = Math.abs(dax) - bA.hw - (m00 * bB.hw + m10 * bB.hh);
        let fAy = Math.abs(day) - bA.hh - (m01 * bB.hw + m11 * bB.hh);
        if (fAx > 0.0 || fAy > 0.0) return;
        let fBx = Math.abs(dbx) - bB.hw - (m00 * bA.hw + m01 * bA.hh);
        let fBy = Math.abs(dby) - bB.hh - (m10 * bA.hw + m11 * bA.hh);
        if (fBx > 0.0 || fBy > 0.0) return;
        let axis = 0;
        let separation = fAx;
        if (dax > 0.0) this.n.set(bA.cos, bA.sin);
        else this.n.set(-bA.cos, -bA.sin);
        if (fAy > this.relativeTol * separation + this.absoluteTol * bA.hh) {
            axis = 1;
            separation = fAy;
            if (day > 0.0) this.n.set(-bA.sin, bA.cos);
            else this.n.set(bA.sin, -bA.cos);
        }
        if (fBx > this.relativeTol * separation + this.absoluteTol * bB.hw) {
            axis = 2;
            separation = fBx;
            if (dbx > 0.0) this.n.set(bB.cos, bB.sin);
            else this.n.set(-bB.cos, -bB.sin);
        }
        if (fBy > this.relativeTol * separation + this.absoluteTol * bB.hh) {
            axis = 3;
            separation = fBy;
            if (dby > 0.0) this.n.set(-bB.sin, bB.cos);
            else this.n.set(bB.sin, -bB.cos);
        }
        let front, negSide, posSide, side;
        switch (axis) {
            case 0:
                this.fn.copy(this.n);
                front = bA.p.dot(this.fn) + bA.hw;
                this.sn.set(-bA.sin, bA.cos);
                side = bA.p.dot(this.sn);
                negSide = -side + bA.hh;
                posSide = side + bA.hh;
                this.computeIncidentEdge(bB);
                break;
            case 1:
                this.fn.copy(this.n);
                front = bA.p.dot(this.fn) + bA.hh;
                this.sn.set(bA.cos, bA.sin);
                side = bA.p.dot(this.sn);
                negSide = -side + bA.hw;
                posSide = side + bA.hw;
                this.computeIncidentEdge(bB);
                break;
            case 2:
                this.fn.copy(this.n).neg();
                front = bB.p.dot(this.fn) + bB.hw;
                this.sn.set(-bB.sin, bB.cos);
                side = bB.p.dot(this.sn);
                negSide = -side + bB.hh;
                posSide = side + bB.hh;
                this.computeIncidentEdge(bA);
                break;
            case 3:
                this.fn.copy(this.n).neg();
                front = bB.p.dot(this.fn) + bB.hh;
                this.sn.set(bB.cos, bB.sin);
                side = bB.p.dot(this.sn);
                negSide = -side + bB.hw;
                posSide = side + bB.hw;
                this.computeIncidentEdge(bA);
                break;
        }
        if (this.clipSegmentToLine(this.c1, this.ie, this.sn.neg(), negSide) < 2)
            return;
        if (this.clipSegmentToLine(this.c2, this.c1, this.sn.neg(), posSide) < 2)
            return;
        let i, cpx, cpy, ct;
        let friction = Math.sqrt(bA.friction * bB.friction);
        for (i = 0; i < 2; ++i) {
            this.sn.copy(this.c2[i]);
            separation = this.fn.dot(this.sn) - front;
            if (separation < 0) {
                if (this.numContacts < this.maxContacts) {
                    ct = this.contacts[this.numContacts];
                } else {
                    ct = new World.Contact(this);
                    this.contacts.push(ct);
                    this.maxContacts++;
                }
                this.sn.subScale(this.fn, separation);
                ct.update(bA, bB, separation, this.n, friction, this.sn);
                this.numContacts++;
            }
        }
    }
    clipSegmentToLine(vO, vI, n, offset) {
        let numOut = 0;
        let distance0 = n.dot(vI[0]) - offset;
        let distance1 = n.dot(vI[1]) - offset;
        if (distance0 <= 0.0) {
            vO[numOut].copy(vI[0]);
            numOut++;
        }
        if (distance1 <= 0.0) {
            vO[numOut].copy(vI[1]);
            numOut++;
        }
        if (distance0 * distance1 < 0.0) {
            let interp = distance0 / (distance0 - distance1);
            vO[numOut].set(
                vI[0].x + interp * (vI[1].x - vI[0].x),
                vI[0].y + interp * (vI[1].y - vI[0].y)
            );
            ++numOut;
        }
        return numOut;
    }
    computeIncidentEdge(b) {
        let nrx = -(b.cos * this.fn.x + b.sin * this.fn.y);
        let nry = -(-b.sin * this.fn.x + b.cos * this.fn.y);
        if (Math.abs(nrx) > Math.abs(nry)) {
            if (nrx > 0.0) {
                this.ie[0].set(b.hw, -b.hh);
                this.ie[1].set(b.hw, b.hh);
            } else {
                this.ie[0].set(-b.hw, b.hh);
                this.ie[1].set(-b.hw, -b.hh);
            }
        } else {
            if (nry > 0.0) {
                this.ie[0].set(b.hw, b.hh);
                this.ie[1].set(-b.hw, b.hh);
            } else {
                this.ie[0].set(-b.hw, -b.hh);
                this.ie[1].set(b.hw, -b.hh);
            }
        }
        this.ie[0].set(
            b.p.x + b.cos * this.ie[0].x - b.sin * this.ie[0].y,
            b.p.y + b.sin * this.ie[0].x + b.cos * this.ie[0].y
        );
        this.ie[1].set(
            b.p.x + b.cos * this.ie[1].x - b.sin * this.ie[1].y,
            b.p.y + b.sin * this.ie[1].x + b.cos * this.ie[1].y
        );
    }
}
////////////////////////////////////////////////////
World.Vec = class {
    constructor(x, y) {
        this.x = x || 0.0;
        this.y = y || 0.0;
    }
    set(x, y) {
        this.x = x;
        this.y = y;
        return this;
    }
    copy(a) {
        this.x = a.x;
        this.y = a.y;
        return this;
    }
    dot(a) {
        return this.x * a.x + this.y * a.y;
    }
    cross(a) {
        return this.x * a.y - this.y * a.x;
    }
    sqlen() {
        return this.x * this.x + this.y * this.y;
    }
    neg() {
        this.x = -this.x;
        this.y = -this.y;
        return this;
    }
    perp() {
        const x = this.x;
        this.x = this.y;
        this.y = -x;
        return this;
    }
    add(a) {
        this.x += a.x;
        this.y += a.y;
        return this;
    }
    addScale(a, s) {
        this.x += a.x * s;
        this.y += a.y * s;
        return this;
    }
    subScale(a, s) {
        this.x -= a.x * s;
        this.y -= a.y * s;
        return this;
    }
    scaleFrom(a, s) {
        this.x = a.x * s;
        this.y = a.y * s;
        return this;
    }
    subFrom(a, b) {
        this.x = a.x - b.x;
        this.y = a.y - b.y;
        return this;
    }
}

function vec(x, y) {
    return new World.Vec(x, y);
}
///////////////////////////////////////////////
World.Contact = class {
    constructor(world) {
        this.bA = null;
        this.bB = null;
        this.p = vec();
        this.n = vec();
        this.r1 = vec();
        this.r2 = vec();
        this.rv = vec();
        this.ip = vec();
        this.Pn = 0.0;
        this.Pt = 0.0;
        this.separation = 0.0;
        this.massNormal = 0.0;
        this.massTangent = 0.0;
        this.bias = 0.0;
        this.friction = 0.0;
        this.allowedPenetration = world.allowedPenetration;
        this.biasFactor = -world.biasFactor * world.invDT;
    }
    update(bA, bB, separation, n, friction, p) {
        this.bA = bA;
        this.bB = bB;
        this.separation = separation;
        this.n.copy(n);
        this.p.copy(p);
        this.Pn = 0.0;
        this.Pt = 0.0;
        this.friction = friction;
        this.r1.subFrom(this.p, this.bA.p);
        this.r2.subFrom(this.p, this.bB.p);
        this.massNormal =
            1.0 /
            (this.bA.iM +
                this.bB.iM +
                this.bA.iI * (this.r1.sqlen() - this.r1.dot(this.n) ** 2) +
                this.bB.iI * (this.r2.sqlen() - this.r2.dot(this.n) ** 2));
        this.massTangent =
            1.0 /
            (this.bA.iM +
                this.bB.iM +
                this.bA.iI * (this.r1.sqlen() - this.r1.dot(this.n) ** 2) +
                this.bB.iI * (this.r2.sqlen() - this.r2.dot(this.n) ** 2));
        this.bias =
            this.biasFactor * Math.min(0.0, this.separation + this.allowedPenetration);
    }
    relativeVelocity() {
        this.rv.set(
            this.bB.v.x - this.bB.va * this.r2.y - this.bA.v.x + this.bA.va * this.r1.y,
            this.bB.v.y + this.bB.va * this.r2.x - this.bA.v.y - this.bA.va * this.r1.x
        );
    }
    impulse(ip) {
        this.bA.v.subScale(ip, this.bA.iM);
        this.bA.va -= this.bA.iI * this.r1.cross(ip);
        this.bB.v.addScale(ip, this.bB.iM);
        this.bB.va += this.bB.iI * this.r2.cross(ip);
    }
    applyImpulse() {
        this.relativeVelocity();
        const Pn0 = this.Pn;
        this.Pn = Math.max(
            Pn0 + this.massNormal * (-this.rv.dot(this.n) + this.bias),
            0.0
        );
        this.ip.scaleFrom(this.n, this.Pn - Pn0);
        this.impulse(this.ip);
        this.relativeVelocity();
        const maxPt = this.friction * this.Pn;
        const Pn1 = this.Pt;
        this.Pt = Math.max(-maxPt,
            Math.min(Pn1 - this.massTangent * this.rv.cross(this.n), maxPt)
        );
        this.ip.scaleFrom(this.n, this.Pt - Pn1).perp();
        this.impulse(this.ip);
    }
}
///////////////////////////////////////////////
World.Shape = class {
    constructor(setup) {
        this.ctx = ctx;
        this.width = setup.w || 0.0;
        this.height = setup.h || 0.0;
        this.offset = setup.offset || 0.0;
        this.loaded = false;
        this.texture = null;
        if (!setup.src) return;
        const source = new Image();
        source.onload = e => {
            this.loaded = true;
            this.texture = document.createElement("canvas");
            this.texture.width = source.height;
            this.texture.height = source.width;
            const ict = this.texture.getContext("2d");
            ict.translate(source.height / 2, source.width / 2);
            ict.rotate(setup.rotate);
            ict.drawImage(source, -source.width / 2, -source.height / 2);
        };
        source.src = setup.src;
    }
    draw() {
        this.ctx.drawImage(
            this.texture, -this.width * 0.5, -this.height * 0.5 + this.offset,
            this.width,
            this.height
        );
    }
}
///////////////////////////////////////////////
World.Body = class {
    constructor(world, setup) {
        const w = setup.w || 1.0;
        const h = setup.h || 1.0;
        this.p = vec(setup.x, setup.y);
        this.v = vec(setup.vx, setup.vy);
        this.hw = w * 0.5;
        this.hh = h * 0.5;
        this.rd = Math.sqrt(this.hw * this.hw + this.hh * this.hh);
        this.va = setup.angularVelocity || 0.0;
        this.ra = setup.rotation || 0.0;
        this.cos = Math.cos(this.ra);
        this.sin = Math.sin(this.ra);
        this.friction =
            setup.friction === undefined ? world.friction : setup.friction;
        let mass = setup.mass || Infinity;
        if (mass < Infinity) {
            this.iM = 1.0 / mass;
            this.iI = 1.0 / (mass * (w * w + h * h) / 12);
        } else {
            this.iM = 0.0;
            this.iI = 0.0;
        }
        this.dt = world.timeStep;
        this.gravity = setup.gravity || world.gravity;
        this.ctx = ctx;
        this.world = world;
        this.nocontact = [];
        this.shape = setup.shape ?
            world.shapes[setup.shape] :
            new World.Shape({});
    }
    addJoint(setup) {
        setup.b1 = setup.body;
        setup.b2 = this;
        this.world.addJoint(setup);
        return this;
    }
    integrate() {
        if (!this.iM) return;
        this.p.addScale(this.v, this.dt);
        this.ra += this.va * this.dt;
        this.v.y += this.gravity * this.dt;
        this.cos = Math.cos(this.ra);
        this.sin = Math.sin(this.ra);
        if (this.p.y > this.world.maxy) {
            this.world.removeBody(this);
        }
    }
    draw() {
        if (this.shape.loaded) {
            this.ctx.save();
            this.ctx.translate(this.p.x, this.p.y);
            this.ctx.rotate(this.ra);
            this.shape.draw();
            this.ctx.restore();
        }
        if (this.world.DEBUG) {
            let chw = this.cos * this.hw;
            let shw = this.sin * this.hw;
            let chh = this.cos * this.hh;
            let shh = this.sin * this.hh;
            this.ctx.beginPath();
            this.ctx.moveTo(this.p.x - chw + shh, this.p.y - shw - chh);
            this.ctx.lineTo(this.p.x + chw + shh, this.p.y + shw - chh);
            this.ctx.lineTo(this.p.x + chw - shh, this.p.y + shw + chh);
            this.ctx.lineTo(this.p.x - chw - shh, this.p.y - shw + chh);
            this.ctx.closePath();
            this.ctx.strokeStyle = "#fff";
            this.ctx.stroke();
        }
    }
    noContact(list) {
        for (const b of list) {
            if (!this.nocontact.includes(b)) this.nocontact.push(b);
            if (!b.nocontact.includes(this)) b.nocontact.push(this);
        }
    }
}
///////////////////////////////////////////////
World.Joint = class {
    constructor(world, setup) {
        this.bA = setup.b1;
        this.bB = setup.b2;
        let c, s, x, y;
        c = this.bA.cos;
        s = this.bA.sin;
        x = setup.ax - this.bA.p.x;
        y = setup.ay - this.bA.p.y;
        this.a1 = vec(c * x + s * y, -s * x + c * y);
        c = this.bB.cos;
        s = this.bB.sin;
        x = setup.ax - this.bB.p.x;
        y = setup.ay - this.bB.p.y;
        this.a2 = vec(c * x + s * y, -s * x + c * y);
        this.m00 = 0.0;
        this.m01 = 0.0;
        this.m11 = 0.0;
        this.r1 = vec();
        this.r2 = vec();
        this.bs = vec();
        this.ai = vec();
        this.ip = vec();
        let bias = setup.biasFactor ? setup.biasFactor : world.biasFactor;
        this.biasFactor = -bias * world.invDT;
        this.softness = setup.softness || 0.0;
        this.iM = this.bA.iM + this.bB.iM + this.softness;
        this.color = setup.color || "#888";
        this.ctx = ctx;
    }
    preStep() {
        this.r1.set(
            this.bA.cos * this.a1.x - this.bA.sin * this.a1.y,
            this.bA.sin * this.a1.x + this.bA.cos * this.a1.y
        );
        this.r2.set(
            this.bB.cos * this.a2.x - this.bB.sin * this.a2.y,
            this.bB.sin * this.a2.x + this.bB.cos * this.a2.y
        );
        const Km00 =
            this.iM +
            this.bA.iI * this.r1.y * this.r1.y +
            this.bB.iI * this.r2.y * this.r2.y;
        const Km01 = -this.bA.iI * this.r1.x * this.r1.y - this.bB.iI * this.r2.x * this.r2.y;
        const Km11 =
            this.iM +
            this.bA.iI * this.r1.x * this.r1.x +
            this.bB.iI * this.r2.x * this.r2.x;
        const det = 1.0 / (Km00 * Km11 - Km01 * Km01);
        this.m00 = det * Km11;
        this.m01 = -det * Km01;
        this.m11 = det * Km00;
        this.bs.set(
            (this.bB.p.x + this.r2.x - (this.bA.p.x + this.r1.x)) * this.biasFactor,
            (this.bB.p.y + this.r2.y - (this.bA.p.y + this.r1.y)) * this.biasFactor
        );
        this.bA.v.subScale(this.ai, this.bA.iM);
        this.bA.va -= this.bA.iI * this.r1.cross(this.ai);
        this.bB.v.addScale(this.ai, this.bB.iM);
        this.bB.va += this.bB.iI * this.r2.cross(this.ai);
    }
    applyImpulse() {
        const bx =
            this.bs.x -
            (this.bB.v.x -
                this.bB.va * this.r2.y -
                this.bA.v.x +
                this.bA.va * this.r1.y) -
            this.ai.x * this.softness;
        const by =
            this.bs.y -
            (this.bB.v.y +
                this.bB.va * this.r2.x -
                this.bA.v.y -
                this.bA.va * this.r1.x) -
            this.ai.y * this.softness;
        this.ip.set(this.m00 * bx + this.m01 * by, this.m01 * bx + this.m11 * by);
        this.bA.v.subScale(this.ip, this.bA.iM);
        this.bA.va -= this.bA.iI * this.r1.cross(this.ip);
        this.bB.v.addScale(this.ip, this.bB.iM);
        this.bB.va += this.bB.iI * this.r2.cross(this.ip);
        this.ai.add(this.ip);
    }
}
///////////////////////////////////////////////
class Canvas {
    constructor() {
        this.elem = document.createElement("canvas");
        this.ctx = this.elem.getContext("2d");
        document.body.appendChild(this.elem);
        this.resize();
        window.addEventListener("resize", () => this.resize(), false);
    }
    resize() {
        this.width = this.elem.width = this.elem.offsetWidth;
        this.height = this.elem.height = this.elem.offsetHeight;
        if (ground) {
            ground.p.x = this.width / 2
            ground.hw = Math.min(this.width, this.height) * 0.5;
            ground.rd = Math.sqrt(ground.hw * ground.hw + ground.hh * ground.hh);
        }
    }
}
///////////////////////////////////////////////
const human = (x, y) => {
    const human = [];
    const armleft = world.addBody({
        x: x,
        y: y + s * 2,
        w: s * 0.75,
        h: s * 2,
        mass: 1,
        shape: "arm"
    });
    human.push(armleft);
    const forearmleft = world.addBody({
        x: x,
        y: y + s * 4,
        w: s * 0.5,
        h: s * 2.5,
        mass: 1,
        shape: "forearm"
    });
    human.push(forearmleft);
    const legleft = world.addBody({
        x: x - s * 0.25,
        y: y + s * 5.25,
        w: s * 1,
        h: s * 2.5,
        mass: 2,
        shape: "leg"
    });
    human.push(legleft);
    const forelegleft = world.addBody({
        x: x - s * 0.25,
        y: y + s * 7.5,
        w: s * 1,
        h: s * 3,
        mass: 1,
        shape: "foreleg"
    });
    human.push(forelegleft);
    const pleft = world.addBody({
        x: x + s * 0.25,
        y: y + s * 6,
        w: s * 0.01,
        h: s * 1,
        mass: 2
    }).addJoint({
        body: legleft,
        ax: x + s * 0.25,
        ay: y + s * 6,
        softness: 0.5
    });
    human.push(pleft);
    const legright = world.addBody({
        x: x - 1,
        y: y + s * 5.25,
        w: s * 1,
        h: s * 2.5,
        mass: 2,
        shape: "leg"
    });
    human.push(legright);
    const forelegright = world.addBody({
        x: x - 1,
        y: y + s * 7.5,
        w: s * 1,
        h: s * 3,
        mass: 1,
        shape: "foreleg"
    });
    human.push(forelegright);
    const pright = world
        .addBody({
            x: x + s * 0.25,
            y: y + s * 6,
            w: s * 0.01,
            h: s * 1,
            mass: 2
        })
        .addJoint({
            body: legright,
            ax: x + s * 0.25,
            ay: y + s * 6,
            softness: 0.5
        });
    human.push(pright);
    const head = world.addBody({
        x: x,
        y: y,
        w: s,
        h: s * 1.5,
        mass: 1,
        gravity: -50,
        shape: "head"
    });
    human.push(head);
    const torso = world.addBody({
        x: x,
        y: y + s * 2.75,
        w: s * 1.5,
        h: s * 3.5,
        mass: 5,
        angularVelocity: Math.random() * 50 - 25,
        shape: "torso"
    });
    human.push(torso);
    const tright = world.addBody({
        x: x - s * 0.75,
        y: y + s * 3.75,
        w: s * 0.2,
        h: s * 1,
        mass: 2
    }).addJoint({
        body: torso,
        ax: x - s * 0.75,
        ay: y + s * 3.75
    });
    human.push(tright);
    const armright = world.addBody({
        x: x + 1,
        y: y + s * 2.3,
        w: s * 1,
        h: s * 2,
        mass: 1,
        shape: "arm"
    });
    human.push(armright);
    const forearmright = world.addBody({
        x: x + 1,
        y: y + s * 4,
        w: s * 0.5,
        h: s * 2.6,
        mass: 1,
        shape: "forearm"
    });
    human.push(forearmright);
    torso.addJoint({
        body: head,
        ax: x,
        ay: y + s,
        softness: 0.5
    });
    armright.addJoint({
        body: torso,
        ax: x + 1,
        ay: y + s,
        softness: 0.5
    });
    forearmright.addJoint({
        body: armright,
        ax: x + 1,
        ay: y + s * 3,
        softness: 0.5
    });
    armleft.addJoint({
        body: torso,
        ax: x,
        ay: y + s,
        softness: 0.5
    });
    forearmleft.addJoint({
        body: armleft,
        ax: x,
        ay: y + s * 3,
        softness: 0.5
    });
    legright.addJoint({
        body: torso,
        ax: x - 1,
        ay: y + s * 4.25,
        softness: 0.5
    });
    forelegright.addJoint({
        body: legright,
        ax: x - 1,
        ay: y + s * 6.26,
        softness: 0.5
    });
    legleft.addJoint({
        body: torso,
        ax: x - s * 0.25,
        ay: y + s * 4.25,
        softness: 0.5
    });
    forelegleft.addJoint({
        body: legleft,
        ax: x - s * 0.25,
        ay: y + s * 6.26,
        softness: 0.5
    });
    armright.noContact([torso, tright, head, forearmright, forearmleft])
    forearmright.noContact([tright, forearmleft]);
    armleft.noContact([head, torso, tright, armright, forearmleft, forearmright]);
    legleft.noContact([torso, legright, forelegleft, forelegright, pright]);
    legright.noContact([torso, forelegright, forelegleft, pleft]);
    forelegleft.noContact([legleft, pright, forelegright]);
    forelegright.noContact([pleft]);
    return human;
};
///////////////////////////////////////////////
let ground = null;
const humans = [];
const canvas = new Canvas();
const ctx = canvas.ctx,
    width = canvas.width,
    height = canvas.height;
let frame = 0;
if (window.location.href.indexOf("fullcpgrid") > -1) {
    document.addEventListener('DOMContentLoaded', function() {
        const original = document.createElement('iframe');
        original.style.display = 'none';
        document.body.appendChild(original);
        window.requestAnimationFrame = original.contentWindow.requestAnimationFrame;
    }, false);
}
///////////////////////////////////////////////
function run() {
    requestAnimationFrame(run);
    if (frame++ % 120 === 0) {
        humans.push(human(
            canvas.width / 2, -canvas.height / 2.5
        ));
    }
    ground.p.y += (canvas.height + 50 - ground.p.y) * 0.05;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    world.step();
}
///////////////////////////////////////////////
const world = new World({
    gravity: 30,
    iterations: 9,
    timeStep: 1 / 30,
    friction: 0.5,
    maxy: height * 2
});
ground = world.addBody({
    x: width / 2,
    y: height + 50,
    w: Math.min(width, height),
    h: 100,
    mass: Infinity
});
///////////////////////////////////////////////
const s = height / 30;
world.addShape({
    id: "head",
    w: s * 1.6,
    h: s * 2.1,
    offset: s * 0.2,
    rotate: -Math.PI / 2,
    src: "img/r5.png"
});
world.addShape({
    id: "torso",
    w: s * 2.2,
    h: s * 3.75,
    offset: -s * 0.1,
    rotate: Math.PI / 2,
    src: "img/r1.png"
});
world.addShape({
    id: "arm",
    w: s * 1.3,
    h: s * 2.4,
    offset: -s * 0.35,
    rotate: Math.PI / 2,
    src: "img/r2.png"
});
world.addShape({
    id: "forearm",
    w: s * 1.2,
    h: s * 2.6,
    offset: s * 0,
    rotate: Math.PI / 2,
    src: "img/r3.png"
});
world.addShape({
    id: "leg",
    w: s * 1.5,
    h: s * 2.8,
    offset: 0,
    rotate: Math.PI / 2,
    src: "img/r4.png"
});
world.addShape({
    id: "foreleg",
    w: s * 2.2,
    h: s * 3,
    offset: s * 0,
    rotate: Math.PI / 2,
    src: "img/r6.png"
});
run();</script>

</body>
</html>
